package com.weijun.keepalive_daemon.process

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.os.Parcel
import android.os.Process
import android.os.RemoteException
import com.weijun.keepalive_daemon.DaemonKeepAlive
import com.weijun.keepalive_daemon.EmptyInstrumentation
import com.weijun.keepalive_daemon.NativeLib
import com.weijun.keepalive_daemon.createNewFile
import com.weijun.keepalive_daemon.logE
import java.io.File
import java.io.IOException

/**
 * @author: weijun
 * date: 2023/7/6
 * description:
 */
internal class ProcessImpl : IProcess {

    companion object {
        const val INDICATOR_DIR_NAME = "5FAD3EB1C66A40ADAB5FDEBE67B53F6F"
        const val INDICATOR_PERSISTENT_FILENAME = "file1"
        const val INDICATOR_DAEMON_ASSISTANT_FILENAME = "file2"
        const val OBSERVER_PERSISTENT_FILENAME = "file3"
        const val OBSERVER_DAEMON_ASSISTANT_FILENAME = "file4"
    }

    private lateinit var mApp:ComponentName
    private lateinit var mRemote:IBinder
    private lateinit var mServiceData:Parcel
    private val mPid = Process.myPid()

    override fun init(context: Context): Boolean = initIndicatorFiles(context)

    private fun initIndicatorFiles(context: Context): Boolean {
        "ProcessImpl init".logE()
        val dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE)
        if (!dirFile.exists()) {
            dirFile.mkdirs()
        }
        return try {
            dirFile createNewFile INDICATOR_PERSISTENT_FILENAME
            dirFile createNewFile INDICATOR_DAEMON_ASSISTANT_FILENAME
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        }
    }

    override fun onPersistentCreate(context: Context, configs: DaemonConfigs) {
        "ProcessImpl onPersistentCreate".logE()
        initAmsBinder()
        initServiceParcel(context, configs.DAEMON_ASSISTANT_CONFIG.serviceName)
        startServiceByAmsBinder()
        Thread {
            val indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE)
            NativeLib().doDaemon(
                File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).absolutePath,
                File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).absolutePath,
                File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).absolutePath,
                File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).absolutePath
            )
        }.start()
    }

    override fun onDaemonAssistantCreate(context: Context, configs: DaemonConfigs) {
        "ProcessImpl onDaemonAssistantCreate".logE()
        initAmsBinder()
        initServiceParcel(context, configs.PERSISTENT_CONFIG.serviceName)
        startServiceByAmsBinder()
        Thread {
            val indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE)
            NativeLib().doDaemon(
                File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).absolutePath,
                File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).absolutePath,
                File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).absolutePath,
                File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).absolutePath
            )
        }.start()
    }

    override fun onDaemonDead() {
        "ProcessImpl onDaemonDead".logE()
        if (startServiceByAmsBinder()) {
            "mPid: $mPid ---> current pid: ${Process.myPid()}".logE()
            DaemonKeepAlive.application?.startInstrumentation(mApp, null, null)
            Process.killProcess(mPid)
        }
    }

    //进程死了，节省时间重启和杀死自己，不要浪费时间去回收
    private fun initServiceParcel(context: Context, serviceName: String) {
        val intent = Intent().apply {
            component = ComponentName(context.packageName, serviceName)
            writeToParcel(Parcel.obtain(), 0)
        }
        mServiceData = Parcel.obtain().apply {
            if (Build.VERSION.SDK_INT >= 26) {
                // Android 8.1
                writeInterfaceToken("android.app.IActivityManager")
                writeStrongBinder(null)
                writeInt(1)
                intent.writeToParcel(this, 0)
                writeString(null)
                writeInt(if (context.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) 1 else 0)
                writeString(context.packageName)
                writeInt(0)
            } else {
                // http://aospxref.com/android-7.1.2_r36/xref/frameworks/base/core/java/android/app/ActivityManagerNative.java
                writeInterfaceToken("android.app.IActivityManager")
                writeStrongBinder(null)
                intent.writeToParcel(this, 0)
                writeString(null)
                writeString(context.packageName)
                writeInt(0)
            }
        }
    }

    private fun initAmsBinder() {
        if (DaemonKeepAlive.application == null) {
            "请先初始化 DaemonKeepAlive".logE()
            return
        }
        mApp = ComponentName(DaemonKeepAlive.application!!, EmptyInstrumentation::class.java)
        try {
            val activityManagerNative = Class.forName("android.app.ActivityManagerNative")
            val amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative)
            val mRemoteField = amn.javaClass.getDeclaredField("mRemote")
            mRemoteField.isAccessible = true
            mRemote = mRemoteField[amn] as IBinder
        } catch (e: Throwable) {
            e.printStackTrace()
        }
    }

    private fun startServiceByAmsBinder(): Boolean {
        return try {
            if (::mRemote.isInitialized.not() || ::mServiceData.isInitialized.not()) {
                "mRemote 或者 mServiceData 未初始化".logE()
                return false
            }
            val code = when (Build.VERSION.SDK_INT) {
                26, 27 -> 26
                28 -> 30
                29 -> 24
                else -> 34
            }
            mRemote.transact(code, mServiceData, null, 1) //START_SERVICE_TRANSACTION = 34
            true
        } catch (e: RemoteException) {
            e.printStackTrace()
            false
        }
    }
}